package mm.basic_syntax

import mm.utils.pd
import mm.utils.pl
import org.junit.Test

/**
 * Descriptions：基础语法
 * <p>
 * Author：ChenME
 * Date：2017/6/2
 * Email：ibelieve1210@163.com
 */
class BasicSyntax {

    /**
     * 返回Int类型的函数
     */
    fun sum(a: Int, b: Int): Int {
        return a + b
    }

    /**
     * 只有一个表达式的函数体的函数
     */
    fun sum1(a: Int, b: Int): Int = a + b

    /**
     * 当函数体只有一个表达式时，可省略返回值类型（）
     */
    fun sum2(a: Int, b: Int) = a + b

    /**
     * 无返回值的函数
     */
    fun printSum(a: Int, b: Int): Unit = pl("printSum --> sum of $a and $b is " + (a + b))

    /**
     * 无返回值的函数，Unit可省略
     */
    fun printSum1(a: Int, b: Int) = pl("printSum1 --> sum of $a and $b is " + (a + b))

    /**
     * 函数测试
     */
    @Test fun test_fun() {
        pl("sum --> sum of 1 and 2 is " + sum(1, 2))
        pl("sum1 --> sum of 1 and 2 is " + sum1(1, 2))
        pl("sum2 --> sum of 1 and 2 is " + sum2(1, 2))
        printSum(1, 2)
        printSum1(1, 2)
    }

    /** ------------------------------------------------------------------------- */

    /**
     * 定义变量
     */
    @Test fun test_definingLocalVariables() {

        /**
         * 只读局部变量
         */
        val a: Int = 1  // 直接赋值
        val b = 2   // Int 类型可省略，系统自动推测
        val c: Int  // 当没有直接赋值时需要指定类型
        c = 3       // deferred assignment
        pl("a = $a, b = $b, c = $c")

        /**
         * 可变变量
         */
        var x = 5 // `Int` type is inferred
        x += 1
        pl("x = $x")
    }

    /** ------------------------------------------------------------------------- */

    /**
     * 传统的if语句
     */
    fun maxOf(a: Int, b: Int): Int {
        if (a > b) {
            return a
        } else {
            return b
        }
    }

    /**
     * 精简版的if语句
     */
    fun maxOf1(a: Int, b: Int): Int = if (a > b) a else b

    /**
     * 条件表达式
     */
    @Test fun test_useConditionalExpressions() {
        pl("maxOf --> max of 0 and 42 is ${maxOf(0, 42)}")
        pl("maxOf1 --> max of 0 and 42 is ${maxOf1(0, 42)}")
    }

    /** ------------------------------------------------------------------------- */

    /**
     * Int? 表示返回值为Int或null
     */
    fun parseInt(str: String): Int? = str.toIntOrNull()

    fun printProduct(arg1: String, arg2: String) {
        val x = parseInt(arg1)
        val y = parseInt(arg2)

        if (x == null) {
            pl("Wrong number format in arg1: '$arg1'")
            return
        }
        if (y == null) {
            pl("Wrong number format in arg2: '$arg2'")
            return
        }

        pl(x * y)
    }


    @Test fun test_nullableValues() {
        printProduct("6", "7")
        printProduct("a", "7")
        printProduct("a", "b")
    }

    /** ------------------------------------------------------------------------- */

    fun getStringLength(obj: Any): Int? {
        if (obj is String) {
            // `obj` is automatically cast to `String` in this branch
            // 在该分支中‘obj’自动转换成‘String’
            return obj.length
        }

        // `obj` is still of type `Any` outside of the type-checked branch
        // 在类型检查分支之外‘obj’仍然是‘Any’类型
        return null
    }

    fun getStringLength1(obj: Any): Int? {

        if (obj !is String) return null

        // `obj` is automatically cast to `String` in this branch
        // 在该分支中‘obj’自动转换成‘String’
        return obj.length
    }

    fun getStringLength2(obj: Any): Int? {

        // `obj` is automatically cast to `String` on the right-hand side of `&&`
        // 在`&&`右边‘obj’自动转换成‘String’
        if (obj is String && obj.length > 0) return obj.length

        return null
    }


    /**
     * 判断变量的类型（判断成功之后变量会自动转换为相应的类型）
     */
    @Test fun test_typeCheckAndAutomaticCasts() {
        fun printLength(obj: Any) {
//            mm.utils.pl("'$obj' string length is ${getStringLength(obj) ?: "... err, not a string"} ")
            pl("'$obj' string length is ${getStringLength1(obj) ?: "... err, not a string"} ")
//            mm.utils.pl("'$obj' string length is ${getStringLength2(obj) ?: "... err, not a string"} ")
        }
        printLength("我是字符串")
        printLength(1000)
        printLength(listOf(Any()))
    }

    /** ------------------------------------------------------------------------- */

    fun loopFor0() {
        val items = listOf("apple", "banana", "kiwi")
        for (item in items) {
            pl(item)
        }
    }

    fun loopFor1() {
        val items = listOf("apple", "banana", "kiwi")
        for (index in items.indices) {
            pl("item at $index is ${items[index]}")
        }
    }

    fun loopWhile() {
        val items = listOf("apple", "banana", "kiwi")
        var index = 0
        while (index < items.size) {
            pl("item at $index is ${items[index]}")
            index++
        }
    }

    @Test fun test_loop() {
//        loopFor0()
//        loopFor1()
        loopWhile()
    }

    /** ------------------------------------------------------------------------- */

    fun describe(obj: Any?): String =
            when (obj) {
                1 -> "One"
                "Hello" -> "Greeting"
                is Long -> "Long"
                !is String -> "Not a string"
                else -> "Unknown"
            }

    @Test fun test_whenExpression() {
        pl("1 --> ${describe(1)}")
        pl("Hello --> ${describe("Hello")}")
        pl("1L --> ${describe(1L)}")
        pl("null --> ${describe(null)}")
    }

    /** ------------------------------------------------------------------------- */

    @Test fun test_ranges() {
        val x = 10
        val y = 9
        if (x in 1..y + 1) {
            pl("fits in range")
        }

        pd()
        val list = listOf("a", "b", "c")
        if (-1 !in 0..list.lastIndex) {
            pl("-1 is out of range")
        }
        if (list.size !in list.indices) {
            pl("list size is out of valid list indices range too")
        }

        pd()
        for (x in 1..5) {
            print("$x ")
        }

        pl("\n")
        for (x in 1..10 step 2) {
            print("$x ")
        }

        pl("\n")
        for (x in 9 downTo 1 step 3) {
            print("$x ")
        }
    }

    /** ------------------------------------------------------------------------- */

    @Test fun test_collections() {
        var items = setOf("apple", "banana", "kiwi")
        when {
            "orange" in items -> pl("juicy")
            "apple" in items -> pl("apple is fine too")
        }

        pd()
        val fruits = listOf("banana", "avocado", "apple", "kiwi")
        fruits.filter { it.startsWith("a") }
                .sortedBy { it }//正序排序
                .map { it.toUpperCase() }
                .forEach { pl(it) }

        pd()
        val numbers = listOf(1, 101, 23, 15, 3, -1, 0, 200, 34, 11)
        numbers.filter { it > 10 }
                .sortedByDescending { it }//倒序排序
                .forEach { pl(it) }

        pd()
        val map = mapOf("a" to "①", "b" to "②", "c" to 3)
        map.forEach { value, key -> print("key is $key ,value is $value\n") }
    }

    /** ------------------------------------------------------------------------- */

    @Test fun test_null() {
        var random: Double = Math.random()
        print("random is $random --> ")
        var bool: Boolean? = if (random > 0.5f) true else null
        bool?.let {
            pl("bool is $bool, random is $random")
        }
    }
}